#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Git Webhook服务器
用于接收来自Git仓库的推送事件并触发自动部署
"""

import os
import sys
import json
import hmac
import hashlib
import subprocess
import logging
from datetime import datetime
from logging.handlers import RotatingFileHandler
from flask import Flask, request, jsonify, abort

# 配置变量
WEBHOOK_HOST = os.environ.get('WEBHOOK_HOST', '0.0.0.0')
WEBHOOK_PORT = int(os.environ.get('WEBHOOK_PORT', 8008))
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', '')
DEPLOY_SCRIPT = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 
                           'scripts', 'auto_deploy_enhanced.sh')
LOG_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs')
LOG_FILE = os.path.join(LOG_DIR, 'webhook.log')

# 确保日志目录存在
os.makedirs(LOG_DIR, exist_ok=True)

# 设置日志
logger = logging.getLogger('webhook')
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

# 创建文件处理器
file_handler = RotatingFileHandler(LOG_FILE, maxBytes=10*1024*1024, backupCount=5)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

# 初始化Flask应用
app = Flask(__name__)

def log_message(message, level="INFO"):
    """记录日志消息"""
    timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    log_msg = f"[{timestamp}] [{level}] {message}"
    
    if level == "INFO":
        logger.info(message)
    elif level == "WARNING":
        logger.warning(message)
    elif level == "ERROR":
        logger.error(message)
    else:
        logger.info(message)

def verify_signature(payload_body, signature_header):
    """验证请求签名"""
    if not WEBHOOK_SECRET:
        log_message("未设置Webhook密钥，跳过签名验证", "WARNING")
        return True
    
    if not signature_header:
        log_message("请求中缺少签名头", "ERROR")
        return False
    
    # 提取签名算法和签名值
    algorithm, signature = signature_header.split('=', 1)
    if algorithm != 'sha1':
        log_message(f"不支持的签名算法: {algorithm}", "ERROR")
        return False
    
    # 计算HMAC
    mac = hmac.new(WEBHOOK_SECRET.encode(), msg=payload_body, digestmod=hashlib.sha1)
    expected_signature = mac.hexdigest()
    
    # 比较签名
    if not hmac.compare_digest(signature, expected_signature):
        log_message("签名验证失败", "ERROR")
        return False
    
    return True

def deploy_project():
    """执行部署脚本"""
    try:
        if not os.path.exists(DEPLOY_SCRIPT):
            log_message(f"部署脚本不存在: {DEPLOY_SCRIPT}", "ERROR")
            return False, "部署脚本不存在"
        
        log_message(f"开始执行部署脚本: {DEPLOY_SCRIPT}")
        
        # 执行部署脚本
        process = subprocess.Popen(
            ['/bin/bash', DEPLOY_SCRIPT],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True
        )
        
        stdout, stderr = process.communicate()
        
        # 记录脚本输出
        if stdout:
            log_message("部署脚本输出:\n" + stdout)
        
        if stderr:
            log_message("部署脚本错误:\n" + stderr, "WARNING")
        
        if process.returncode == 0:
            log_message("部署脚本执行成功")
            return True, "部署成功"
        else:
            log_message(f"部署脚本执行失败，返回代码: {process.returncode}", "ERROR")
            return False, f"部署失败，返回代码: {process.returncode}"
    
    except Exception as e:
        log_message(f"执行部署脚本时发生错误: {str(e)}", "ERROR")
        return False, f"执行部署时发生错误: {str(e)}"

@app.route('/')
def index():
    """主页路由"""
    return jsonify({
        'status': 'running',
        'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        'message': 'Git Webhook服务正在运行'
    })

@app.route('/webhook', methods=['POST'])
def webhook():
    """处理webhook请求"""
    # 验证Content-Type
    if request.headers.get('Content-Type') != 'application/json':
        log_message("请求Content-Type必须为application/json", "ERROR")
        abort(400)
    
    # 获取并验证签名
    signature_header = request.headers.get('X-Hub-Signature')
    if signature_header and not verify_signature(request.data, signature_header):
        log_message("签名验证失败", "ERROR")
        abort(401)
    
    # 解析请求数据
    try:
        event = request.headers.get('X-GitHub-Event', request.headers.get('X-Gitee-Event', 'push'))
        payload = request.json
    except Exception as e:
        log_message(f"解析请求数据失败: {str(e)}", "ERROR")
        abort(400)
    
    # 记录请求信息
    log_message(f"收到{event}事件")
    
    # 处理push事件
    if event == 'push':
        try:
            # 获取分支信息
            if 'ref' in payload:
                branch = payload['ref'].split('/')[-1]
                repo_name = payload.get('repository', {}).get('name', 'unknown')
                
                log_message(f"仓库: {repo_name}, 分支: {branch}")
                
                # 只处理主要分支
                allowed_branches = ['main', 'master', 'production']
                if branch in allowed_branches:
                    log_message(f"触发分支 {branch} 的自动部署")
                    success, message = deploy_project()
                    
                    if success:
                        return jsonify({
                            'status': 'success',
                            'message': message
                        })
                    else:
                        return jsonify({
                            'status': 'error',
                            'message': message
                        }), 500
                else:
                    log_message(f"忽略非主要分支 {branch} 的推送", "WARNING")
                    return jsonify({
                        'status': 'ignored',
                        'message': f"忽略非主要分支 {branch} 的推送"
                    })
            else:
                log_message("请求数据中缺少ref字段", "ERROR")
                abort(400)
                
        except Exception as e:
            log_message(f"处理push事件时发生错误: {str(e)}", "ERROR")
            abort(500)
    
    # 处理ping事件
    elif event == 'ping':
        return jsonify({
            'status': 'success',
            'message': 'pong'
        })
    
    # 忽略其他事件
    else:
        log_message(f"忽略未处理的事件类型: {event}", "WARNING")
        return jsonify({
            'status': 'ignored',
            'message': f"忽略未处理的事件类型: {event}"
        })

if __name__ == '__main__':
    log_message(f"启动Git Webhook服务器，监听 {WEBHOOK_HOST}:{WEBHOOK_PORT}")
    
    if not WEBHOOK_SECRET:
        log_message("警告: 未设置Webhook密钥，这可能会带来安全风险", "WARNING")
    
    # 检查部署脚本是否存在
    if not os.path.exists(DEPLOY_SCRIPT):
        log_message(f"警告: 部署脚本不存在: {DEPLOY_SCRIPT}", "WARNING")
    
    try:
        app.run(host=WEBHOOK_HOST, port=WEBHOOK_PORT)
    except Exception as e:
        log_message(f"服务器启动失败: {str(e)}", "ERROR")
        sys.exit(1) 